#include "Controlador.h"
#include <fstream>
#include <sys/stat.h>
#include <dirent.h>

static const char*           pathPath = "./path.txt";
static const char*        defaultPath = "./archivos/";
static const char*          spathHash = "hash.bin";
static const char*     spathHashTabla = "hashTabla.bin";
static const char*     spathHashLista = "hashLista.bin";
static const char*         spathArbol = "arbol.bin";
static const char*           spathIdx = "idx.bin";
static const char*            spathMF = "mf.bin";
static const char* spathTOrdAparicion = "terminosPorAparicion.bin";
static const char*  spathPosRelativas = "posRelativas.bin";
static const char*   spathVocabulario = "vocabulario.bin";

using namespace std;

#define TAM_BLOQ_ARB 512
#define REG_P_BLOQ_HASH 20


Controlador::Controlador() : hash(NULL),arbol(NULL),indexMF(NULL),rtt(NULL),estadisticas()
{
	ifstream archPath(pathPath);
	if (!archPath.is_open())
	{
		ofstream tmp(pathPath);
		tmp << defaultPath << "\n";
		pathDir += defaultPath;
	}
	else
	{
		string tmp;
		archPath >> tmp;
		pathDir += tmp;
	}
	umask(0);
	mkdir(pathDir.c_str(),S_IRWXU | S_IRWXG | S_IRWXO); //Si el dir ya existe, esta llamada no hace nada
	abrirArchivos();
}

void Controlador::abrirArchivos()
{
	// Arbol
	string arbPath = pathDir;
	arbPath += spathArbol;
	arbol = new ArbolBMas<ClaveString,DatoInt>(arbPath.c_str(), TAM_BLOQ_ARB, false);

	// Hash
	string hashPath = pathDir;
	string hashTPath = pathDir;
	string hashLPath = pathDir;
	hashPath += spathHash;
	hashTPath += spathHashTabla;
	hashLPath += spathHashLista;
	hash = new hashExtensible<TipoDatoInt,tipoClaveString>
		(hashPath.c_str(), hashTPath.c_str(), hashLPath.c_str(), false, REG_P_BLOQ_HASH);

	// Masterfile indexada
	string idxPath = pathDir;
	string mfPath = pathDir;
	idxPath += spathIdx;
	mfPath += spathMF;
	indexMF = new IdxMasterfile(idxPath.c_str(), mfPath.c_str());

	// RTT
	string termAparicionPath = pathDir;
	string posRelativasPath = pathDir;
	string vocabularioPath = pathDir;

	termAparicionPath += spathTOrdAparicion;
	posRelativasPath += spathPosRelativas;
	vocabularioPath += spathVocabulario;

	rtt = new RTT(termAparicionPath.c_str(), posRelativasPath.c_str(), vocabularioPath.c_str(), false);
}

bool Controlador::indexarArchivo(string &path, RegistroMF &result)
{
	ifstream arch(path.c_str());
	if (!arch.is_open()) return false;
	try	 { result.cargar(arch); }
	catch (exception &e) { return false; }
	//Veo si ya estaba
	if (estaIndexado(result)) return false;
	//Indexo en los varios lugares donde debo
	size_t id = indexMF->agregar(result);
	//Arbol
		//Por cada autor indexo el id con el autor como clave en el arbol
	for(vector<string>::iterator it = result.autores.begin(); it != result.autores.end(); ++it)
	{
		DatoInt *datoArbol = new DatoInt();
		datoArbol->setDato(id);
		ClaveString *claveArbol = new ClaveString();
		claveArbol->setClave(it->c_str());
		int agregarDato = arbol->modificarRegistro(claveArbol, datoArbol);
		if (agregarDato == 5) {
			RegDatos<ClaveString,DatoInt> *regArb = new RegDatos<ClaveString,DatoInt>();
			regArb->setClave(claveArbol);
			regArb->cargarDato(datoArbol);			
			if (arbol->insertarRegistro(regArb) == 3)
			{
				delete regArb;
				string s;
				result.sCabecera(s);
				cout << "Error al insertar en el arbol: " << s << endl;
				return false;
			}
		}			
		
		else {
			delete claveArbol;
			if (agregarDato == 4){
				return false;
			}
		}

	}
	//Hash
	tipoClaveString claveHash(result.titulo.c_str());
	TipoDatoInt datohash(id);
	hash->agregar(&claveHash, &datohash);

	//RTT
	rtt->procesarLetra(result.letra, id);
	rtt->ordenarOcurrencias();
	rtt->armarListasInvertidas();	

	return true;
}

bool Controlador::indexarDirectorio(string &path, vector<RegistroMF> &results)
{
	DIR* dir = opendir(path.c_str());
	if (dir == NULL) return false;
	struct dirent* dirEnt = readdir(dir);
	while(dirEnt != NULL)
	{
		RegistroMF tmp;
		string s(path);
		s.append(dirEnt->d_name);
		bool indexad = indexarArchivo(s, tmp);
		if (indexad)
		{
			results.push_back(tmp);
		}
		dirEnt = readdir(dir);
	}
	closedir(dir);
	return true;
}

void Controlador::buscarTitulo(string &titulo, vector<RegistroMF> &results)
{
	tipoClaveString claveHash(titulo.c_str());
	vector<TipoDatoInt> ids;
	hash->buscar(&claveHash, &ids);
	for(vector<TipoDatoInt>::iterator it = ids.begin(); it != ids.end(); ++it)
	{
		RegistroMF tmp;
		indexMF->recuperar(it->getDato(), tmp);
		results.push_back(tmp);
	}
}

void Controlador::buscarAutor(string &autor, vector<RegistroMF> &results)
{
	ClaveString clave;
	clave.setClave(autor.c_str());
	RegDatos<ClaveString,DatoInt>* resBusqueda = arbol->buscar(&clave);
	if (!resBusqueda) return;
	if (resBusqueda->getClave()->getClave() != autor)
	{
		delete resBusqueda;
		return;
	}
	list<DatoInt*> listaDat = resBusqueda->getDatos();
	for(list<DatoInt*>::iterator it = listaDat.begin(); it != listaDat.end(); ++it)
	{
		RegistroMF tmp;
		indexMF->recuperar((*it)->getDato(), tmp);
		results.push_back(tmp);
	}
	delete resBusqueda;
}

void Controlador :: buscarFrase(string &frase, vector<RegistroMF> &results)
{
	vector<int> ids;

	(*rtt).buscarPorFrase(frase, ids);
	
	for(vector<int>::iterator it = ids.begin(); it != ids.end(); ++it)
	{
		RegistroMF tmp;
		indexMF->recuperar(*it, tmp);
		results.push_back(tmp);
	}
}

bool Controlador::estaIndexado(RegistroMF &reg)
{
	vector<size_t> idsFinales;
	//Busco por titulo
	tipoClaveString claveHash(reg.titulo.c_str());
	vector<TipoDatoInt> idsHash;
	hash->buscar(&claveHash, &idsHash);
	//Busco por un solo autor, pues el resultado deberia aparecer lo mismo
	ClaveString clave;
	clave.setClave(reg.autores[0].c_str());
	RegDatos<ClaveString,DatoInt>* resBusqueda = arbol->buscar(&clave);
	if (!resBusqueda) return false;
	if (resBusqueda->getClave()->getClave() != reg.autores[0])
	{
		delete resBusqueda;
		return false;
	}
	list<DatoInt*> listaDat = resBusqueda->getDatos();
	//Pongo todos los que esten en las 2 listas de ids en otra lista
	for(list<DatoInt*>::iterator it1 = listaDat.begin(); it1 != listaDat.end(); ++it1)
	{
		for(vector<TipoDatoInt>::iterator it2 = idsHash.begin(); it2 != idsHash.end(); ++it2)
		{
			if (it2->getDato() == (size_t)(*it1)->getDato()) idsFinales.push_back(it2->getDato());
		}
	}
	delete resBusqueda;
	//Levanto los registros de la mf y veo si coincide el idioma
	for(vector<size_t>::iterator it = idsFinales.begin(); it != idsFinales.end(); ++it)
	{
		RegistroMF tmp;
		indexMF->recuperar(*it, tmp);
		if (tmp.igualdadParcial(reg)) return true;
	}
	return false;
}

bool Controlador::cambiarDestino(string &path)
{
	struct stat st;
	lstat(path.c_str(), &st);
	bool isDir = S_ISDIR(st.st_mode);
	if (!isDir) return false;
	pathDir = path;
	ofstream tmp(pathPath);
	tmp << pathDir << "\n";
	delete hash;
	delete arbol;
	delete indexMF;
	delete rtt;
	abrirArchivos();
	return true;
}

void Controlador::reindexar()
{
	//TODO: Algo menos feo
	delete hash;
	delete arbol;
	delete rtt;

	// Arbol
	string arbPath = pathDir;
	arbPath += spathArbol;
	arbol = new ArbolBMas<ClaveString,DatoInt>(arbPath.c_str(), TAM_BLOQ_ARB, true);

	// Hash
	string hashPath = pathDir;
	string hashTPath = pathDir;
	string hashLPath = pathDir;
	hashPath += spathHash;
	hashTPath += spathHashTabla;
	hashLPath += spathHashLista;
	hash = new hashExtensible<TipoDatoInt,tipoClaveString>
		(hashPath.c_str(), hashTPath.c_str(), hashLPath.c_str(), true, REG_P_BLOQ_HASH);

	// RTT
	string termAparicionPath = pathDir;
	string posRelativasPath = pathDir;
	string vocabularioPath = pathDir;

	termAparicionPath += spathTOrdAparicion;
	posRelativasPath += spathPosRelativas;
	vocabularioPath += spathVocabulario;

	rtt = new RTT(termAparicionPath.c_str(), posRelativasPath.c_str(), vocabularioPath.c_str(), true);	

	indexMF->recrearArchivos();
}

Controlador::~Controlador()
{
	delete hash;
	delete arbol;
	delete indexMF;
	delete rtt;
}

